<?php
	/**
	 	Class: Collection
			Loads a array of Model with a defined query.
	**/
	import('core.Model');
	import('data.Options');
	class Collection {
		var $__table__;
		var $__class__;
		var $__has_many__;
		var $__has_one__;
		var $__belongs_to__;
		var $__elements__;
		var $_order_by = FALSE;
		var $perPage = NULL;
		var $page = NULL;
		/**
			Constructor: Collection
				This class can be used to load multiple instances of the same <Model> in a single query.
			
			Parameters:
				String $class - Name of the Model to load.
			
		**/
		function Collection($class){
			if(DB_LOWERCASE_TABLES) $this->__table__ = strtolower($this->__table__);
			$this->__class__ = $class;
			$temp_class = new $class();
			$this->__table__ = $temp_class->__table__;
			$this->__has_many__ = $temp_class->_has_many;
			$this->__belongs_to__ = $temp_class->_belongs_to;
			$this->__has_one__ = $temp_class->_has_one;
		}
		function clear(){
			$this->__elements__ = NULL;
		}
		function add($object){
			if(get_class($object)==$this->__class__){
				if(!is_array($this->__elements__)) $this->__elements__ = array();
				//echo "added <br />";
				$this->__elements__[] = $object;
			}
		}
		function batchSave($maxRows=500){
			$db = new DataBase();
			$db->connect();
			
			$query = new Query($this->__table__,"insert");
			$query->batchMode = TRUE;
			$firstElement = $this->__elements__[0];
			$query->keys = array_keys($firstElement->__buildVars__());
			$e = 0;
			$query->extendedtype = "IGNORE";
			$query->values = array();
			foreach($this->__elements__ as $elmnt){
				//$elmnt->__buildVars__();
				$executed = false;
				$query->values[] = $elmnt;
				//echo join(",",$elmnt->__class_vars__)."<br/>";
				if($e>0 && $e%$maxRows==0){
					$res = $db->execute($query);
					$query->values = array();
					$executed = true;
				}
				$e++;
			}
			//echo $q->build();
			if(!$executed) $res = $db->execute($query);
			//echo $elmnt->save();
		}
		function execute($additionalQuery=array()){
			if(is_array($additionalQuery)){
				$query = new Query($this->__table__);
				$query->select = $this->__table__.".*";
				$query->order = $this->__table__.".id";
				if($this->perPage){
					$query->countFounds(TRUE);
					$query->limit = ($this->page!=null) ? $this->page*$this->perPage.",".$this->perPage : $this->perPage;
					//$query->limit = $this->perPage;
				}
				if(is_array($additionalQuery)){
					foreach($additionalQuery as $key => $value){
						$property = strtolower($key);
						$query->$property = $value;
					}
				}
				$sameNameCounter = 1;
				// BELONGS TO
				foreach($this->__belongs_to__ as $key=>$value){
					//$className = (is_numeric($key)) ? $value : $key;
					$options = (is_numeric($key)) ? array() : Options::parse($value);
					$className = (is_numeric($key))  ? $value : $key;
					$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;
					//(is_numeric($key)) ? $options["connector"] : $key;
					$tableas = (array_key_exists("as", $options)) ? $options["as"] : false;
					$field = (array_key_exists("field", $options)) ? $options["field"] : strToLower($className);
					$resolver = "";
					$as = "";
					if(class_exists($className)){
						$temp_model = new $className();
						$temp_table_name = strToLower($temp_model->__table__);//."s";
						unset($temp_model);
						//$temp_table_name = strToLower($className)."s";
						if(strToLower($className)==strToLower($this->__class__)){
							$resolver = $sameNameCounter;
							$as = " AS ".$temp_table_name.$resolver;
							$sameNameCounter++;
						}
						if($tableas){
							$query->select .= ", ".$tableas.".*";
							$query->join[] = array(
								$type,
								$temp_table_name." AS ".$tableas,
								$tableas.".id = ".$this->__table__.".id_".$field
							  );
						}else{
							$query->select .= ", ".$temp_table_name.$resolver.".*";
							$query->join[] = array(
								$type,
								$temp_table_name.$as,
								$temp_table_name.$resolver.".id = ".$this->__table__.".id_".$field
							  );
						}
						$obj_name = strToLower($className);
					}else{
						trigger_error("Can't load Class [$className]",E_USER_WARNING);
					}
				}
				// HAS ONE
				foreach($this->__has_one__ as $key=>$value){
					//$className = (is_numeric($key)) ? $value : $key;
					$options = (is_numeric($key)) ? array() : Options::parse($value);
					$className = (is_numeric($key))  ? $value : $key;
					$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;
					//(is_numeric($key)) ? $options["connector"] : $key;
					$resolver = "";
					$as = "";
				
					$temp_model = new $className();
					$temp_table_name = strToLower($temp_model->__table__);//."s";
				
					$where = (array_key_exists("where", $options)) ? " AND ".$temp_table_name.".".$options["where"] : "" ;
				
					unset($temp_model);
					//$temp_table_name = strToLower($className)."s";
					if(strToLower($className)==strToLower($this->__class__)){
						$resolver = $sameNameCounter;
						$as = " AS ".$temp_table_name.$resolver;
						$sameNameCounter++;
					}

					$query->select .= ", ".$temp_table_name.$resolver.".*";
					$query->join[] = array(
						$type,
						$temp_table_name.$as,
						$temp_table_name.$resolver.".id_".strToLower($this->__class__)." = ".$this->__table__.".id".$where
						//_".strToLower($className)
					  );
					$obj_name = strToLower($className);
				}
				// HAS MANY
				foreach($this->__has_many__ as $key=>$value){
					$options = (is_numeric($key)) ? array() : Options::parse($value);
					$className = (is_numeric($key)) ? $value : $key;
					$type = (array_key_exists("type", $options)) ? strToUpper($options["type"]) : "LEFT" ;
					$link = (array_key_exists("link", $options)) ? $options["link"] : FALSE ;
				
					if(!$link){
						$temp_model = new $className();
						$temp_table_name = strToLower($temp_model->__table__);//."s";
						unset($temp_model);
						$query->select .= ", ".$temp_table_name.".*";
					
						$query->join[] = array(
								$type,
								$temp_table_name,
								$temp_table_name.".id_".strToLower($this->__class__).
								"=".$this->__table__.".id"
							);
					}else{
						$temp_model = new $className();
						$temp_model_table_name = strToLower($temp_model->__table__);;
						unset($temp_model);
					
						$temp_table_name = strToLower($link);
						$query->select .= ", ".$temp_model_table_name.".*";

						$query->join[] = array(
								$type,
								$temp_table_name,
								$temp_table_name.".id_".strToLower($this->__class__).
								"=".$this->__table__.".id"
							);

						$query->join[] = array(
								$type,
								$temp_model_table_name,
								$temp_model_table_name.".id = ".$temp_table_name.".id_".strToLower($className)
							);
					}
				}
				if($this->_order_by){
					$query->order .= ", ".$this->_order_by;
				}
			}else if(is_string($additionalQuery)){
				$query = $additionalQuery;
			}
			//new dBug($query);
			// DATABASE
			$db = new DataBase();
			$db->connect();
			$res = $db->query($query);
			return $res;
			/*if($res){
				$res->moveCursor(0);
				$this->buildFromResultset($res);
				$sameNameCounter = 1;
				foreach($this->_belongs_to as $key=>$value){
					$className = (is_numeric($key)) ? $value : $key;
					$obj_name = strToLower($className);
					$resolver = "";
					if($obj_name==get_class($this)){
						$resolver = $sameNameCounter;
						$sameNameCounter++;
					}
					$this->$obj_name = new $className();
					$this->$obj_name->buildFromResultset($res,$resolver);
				}					
				$res->beforeFirst();
				$old_ids = array();
				foreach($this->_has_many as $key=>$value){
					$property_name = (is_numeric($key)) ? strToLower($value."s") : strToLower($key."s");
					$this->$property_name = array();
					$old_ids[$property_name] = array();
				}
				while($res->next()){
					foreach($this->_has_many as $key=>$value){
						$property_name = (is_numeric($key)) ? strToLower($value."s") : strToLower($key."s");
						$class = (is_numeric($key)) ? $value : $key;
						$temp_obj = new  $class();
						$id = $res->getData($property_name.".id");
						if(array_search($id,$old_ids[$property_name])===FALSE || $id==NULL){
							$created = $temp_obj->buildFromResultset($res);
							if($created){
								array_push($this->$property_name, $temp_obj);
								array_push($old_ids[$property_name],$id);
							}
						}
					}
				}
			}
			return ($res!=false);*/
		}
		
		/*function execute($query){
			$db = new DataBase();
			$db->connect();
			return $db->query($query);
		}*/
		
		function build($resultSet){
			$class_name = $this->__class__;
			$temp_result = array();
			$old_ids = array();
			$collid = 0;
			if($resultSet){
				$resultSet->beforeFirst();
				while($resultSet->next()){
					$collection_id = $resultSet->getData($this->__table__.".id");
					$collection_identity = ($collection_id!==false);
					$collid = ($collection_identity) ? $collection_id : $collid;
					if(array_key_exists($collid,$temp_result)===false || !$collection_identity){
						$sameNameCounter = 1;
						$newInstance = new $class_name();
						$newInstance->buildFromResultset($resultSet);
						$old_ids[$collid] = array();
						foreach($this->__belongs_to__ as $key=>$value){
							$className = (is_numeric($key)) ? $value : $key;
							$obj_name = strToLower($className);
							$resolver = "";
							$options = (is_numeric($key)) ? array() : Options::parse($value);
							$tableas = (array_key_exists("as", $options)) ? $options["as"] : false;
							$field = (array_key_exists("field", $options)) ? $options["field"] : strToLower($className);
							if($obj_name==strToLower($this->__class__)){
								$resolver = $sameNameCounter;
								$sameNameCounter++;
							}
							if($tableas){
								$newInstance->$field = new $className();
								$newInstance->$field->buildFromResultset($resultSet,$tableas);
							}else if(class_exists($className)){
								$newInstance->$obj_name = new $className();
								$newInstance->$obj_name->buildFromResultset($resultSet,$newInstance->$obj_name->__table__.$resolver);
							}else{
								trigger_error("Can't load Class [$className]",E_USER_WARNING);
							}
						}
						
						foreach($this->__has_one__ as $key=>$value){
							$className = (is_numeric($key)) ? $value : $key;
							$obj_name = strToLower($className);
							$resolver = "";
							if($obj_name==strToLower($this->__class__)){
								$resolver = $sameNameCounter;
								$sameNameCounter++;
							}
							$newInstance->$obj_name = new $className();
							$newInstance->$obj_name->buildFromResultset($resultSet,$resolver);
						}
						
						foreach($this->__has_many__ as $key=>$value){
							$property_name = (is_numeric($key)) ? strToLower($value."s") : strToLower($key."s");
							$newInstance->$property_name = array();
							$old_ids[$collid][$property_name] = array();
						}
						$temp_result[$collid] = $newInstance;
					}
					$new =& $temp_result[$collection_id];
					foreach($this->__has_many__ as $key=>$value){
						$property_name = (is_numeric($key)) ? strToLower($value."s") : strToLower($key."s");
						$class = (is_numeric($key)) ? $value : $key;
						$temp_obj = new  $class();
						$table = $temp_obj->__table__;
						$id = $resultSet->getData($table.".id");
						if(array_search($id,$old_ids[$collection_id][$property_name])===FALSE || $id==NULL){
							$created = $temp_obj->buildFromResultset($resultSet);
							if($created){
								array_push($new->$property_name, $temp_obj);
								array_push($old_ids[$collection_id][$property_name],$id);
							}
						}
					}
					if(!$collection_identity) $collid++;
				}
			}
			
			$result = array();
			foreach($temp_result as $model){
				array_push($result,$model);
			}
			return $result;
		}
		
		
		/**
			Function: find
				Retrieve a list of <Model> from database.
				This method is useful for executing more than once load from the same models. To load only one list use <Collection.load>.
			
			Parameters: 
				[Query, String, Array] $query - <Query> object, String or Array defining the query to be executed. If not specified all models will be loaded.
				
			Returns:
				Array containing models spicified by $class
				
			Usage:
				(start code)
				$models = new Collection("Model");
				$result_array = $models->find(array("where"=>"id=1"));
				(end)
				
			See also:
				<Query>, <Model>
		**/
		function find($query=array()){
			/*if(is_array($query)){
				$tmp_query = new Query($this->__table__);
				foreach($query as $key => $value){
					$tmp_query->$key = $value;
				}
				$query = $tmp_query;
			}*/
			$res = $this->execute($query);
			$this->__elements__ = $this->build($res);
			//echo "(".$this->__class__." : ".$res->getTotal().")";
			return $this->__elements__;
		}
		
		/**
			Function: load
				Retrieve models from database
			
			Parameters: 
				String $class - Name of the Model to load.
				[Query, Array, String] $query - Query object, String or Array defining the query to be executed. If not specified all models will be loaded.
				
			Returns:
				Array containing models spicified by $class
			
			Usage:
				(start code)
				$models = Collection::load("Model",array("where"=>"id=1"));
				(end)
			
			See also:
				<Query>
		**/
		function load($class, $query=array()){
			$temp_list = new Collection($class);
			$temp_list = $temp_list->find($query);
			return $temp_list;
		}
		
		
		
		function delete($class,$where=""){
			$temp_class = new $class();
			$query = new Query($temp_class->__table__,"delete");
			if($where!="") $query->where = $where;
			$db = new DataBase();
			$db->connect();
			$res = $db->query($query);
			return $res;
		}
		
		
	}
?>